package com.coldworks.base.lib.menudrawer;

import com.coldworks.coldjoke.R;

import android.app.Activity;
import android.os.Bundle;
import android.os.Parcelable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;

public class MenuDrawerManager {

	private static final String STATE_LAYOUT = "net.simonvt.widget.MenuDrawerManager.layoutState";

	/**
	 * The Activity the drawer is attached to.
	 */
	private final Activity mActivity;

	/**
	 * The MenuDrawer layout.
	 */
	private MenuDrawer mMenuDrawer;

	/**
	 * The container for the menu view.
	 */
	private ViewGroup mMenuContainer;

	/**
	 * The container for the content view.
	 */
	private ViewGroup mContentContainer;

	/**
	 * The custom menu view set by the user.
	 */
	private View mMenuView;

	/**
	 * The drag mode of the drawer. Can be either
	 * {@link MenuDrawer#MENU_DRAG_CONTENT} or
	 * {@link MenuDrawer#MENU_DRAG_WINDOW}.
	 */
	private int mDragMode = MenuDrawer.MENU_DRAG_CONTENT;

	/**
	 * Indicates whether the menu view has been attached to the Activity's
	 * layout.
	 */
	private boolean mMenuAttached;

	/**
	 * The state passed to {@link #onRestoreDrawerState(Parcelable)}.
	 */
	private Bundle mRestoredState;

	/**
	 * Constructor to use when creating the menu drawer.
	 * 
	 * @param activity
	 *            The activity the menu drawer will be attached to.
	 * @param dragMode
	 *            The drag mode of the drawer. Can be either
	 *            {@link MenuDrawer#MENU_DRAG_CONTENT} or
	 *            {@link MenuDrawer#MENU_DRAG_WINDOW}.
	 */
	public MenuDrawerManager(Activity activity, int dragMode) {
		this(activity, dragMode, MenuDrawer.MENU_POSITION_LEFT);
	}

	/**
	 * Constructor to use when creating the menu drawer.
	 * 
	 * @param activity
	 *            The activity the menu drawer will be attached to.
	 * @param dragMode
	 *            The drag mode of the drawer. Can be either
	 *            {@link MenuDrawer#MENU_DRAG_CONTENT} or
	 *            {@link MenuDrawer#MENU_DRAG_WINDOW}.
	 * @param gravity
	 *            Where to position the menu. Can be either
	 *            {@link MenuDrawer#MENU_POSITION_LEFT} or
	 *            {@link MenuDrawer#MENU_POSITION_RIGHT}.
	 */
	public MenuDrawerManager(Activity activity, int dragMode, int gravity) {
		mActivity = activity;
		mDragMode = dragMode;

		mMenuDrawer = gravity == MenuDrawer.MENU_POSITION_RIGHT ? new RightDrawer(
				activity) : new LeftDrawer(activity);
		mMenuDrawer.setDragMode(dragMode);
		mMenuDrawer.setId(R.id.md__layout);
		mMenuContainer = (ViewGroup) mMenuDrawer.findViewById(R.id.md__menu);
		mContentContainer = (ViewGroup) mMenuDrawer
				.findViewById(R.id.md__content);

		attachMenuLayout();
	}

	/**
	 * Attaches the menu drawer to the activity's layout.
	 */
	private void attachMenuLayout() {
		if (!mMenuAttached) {
			mMenuAttached = true;

			switch (mDragMode) {
			case MenuDrawer.MENU_DRAG_CONTENT:
				attachToContent();
				break;

			case MenuDrawer.MENU_DRAG_WINDOW:
				attachToDecor();
				break;

			default:
				throw new RuntimeException("Unknown menu mode: " + mDragMode);
			}
		}
	}

	/**
	 * Attaches the menu drawer to the content view.
	 */
	private void attachToContent() {
		/**
		 * Do not call mActivity#setContentView. E.g. if using with a
		 * ListActivity, Activity#setContentView is overridden and dispatched to
		 * MenuDrawerManager#setContentView, which then again calls
		 * Activity#setContentView.
		 */
		ViewGroup content = (ViewGroup) mActivity
				.findViewById(android.R.id.content);
		content.removeAllViews();
		content.addView(mMenuDrawer, LayoutParams.MATCH_PARENT,
				LayoutParams.MATCH_PARENT);
	}

	/**
	 * Attaches the menu drawer to the window.
	 */
	private void attachToDecor() {
		ViewGroup decorView = (ViewGroup) mActivity.getWindow().getDecorView();
		ViewGroup decorChild = (ViewGroup) decorView.getChildAt(0);

		decorView.removeAllViews();
		decorView.addView(mMenuDrawer, LayoutParams.MATCH_PARENT,
				LayoutParams.MATCH_PARENT);

		mContentContainer.addView(decorChild, decorChild.getLayoutParams());
	}

	/**
	 * Returns the MenuDrawer layout.
	 * 
	 * @return The MenuDrawer layout.
	 */
	public MenuDrawer getMenuDrawer() {
		return mMenuDrawer;
	}

	/**
	 * Set the active view. If the mdArrowDrawable attribute is set, this View
	 * will have an arrow drawn next to it.
	 * 
	 * @param v
	 *            The active view.
	 */
	public void setActiveView(View v) {
		setActiveView(v, 0);
	}

	/**
	 * Set the active view. If the mdArrowDrawable attribute is set, this View
	 * will have an arrow drawn next to it.
	 * 
	 * @param v
	 *            The active view.
	 * @param position
	 *            Optional position, usually used with ListView.
	 *            v.setTag(R.id.mdActiveViewPosition, position) must be called
	 *            first.
	 */
	public void setActiveView(View v, int position) {
		mMenuDrawer.setActiveView(v, position);
	}

	/**
	 * Toggles the menu open and close.
	 */
	public void toggleMenu() {
		mMenuDrawer.toggleMenu();
	}

	/**
	 * Toggles the menu open and close.
	 * 
	 * @param animate
	 *            Whether open/close should be animated.
	 */
	public void toggleMenu(boolean animate) {
		mMenuDrawer.toggleMenu(animate);
	}

	private OpenMenuListener openMenuListener;
	private CloseMenuListener closeMenuListener;

	public void setOnOpenMenuListener(OpenMenuListener listener) {
		this.openMenuListener = listener;
	}

	public void setOnColseMenuListener(CloseMenuListener listener) {
		this.closeMenuListener = listener;
	}

	public interface OpenMenuListener {
		void onOpenMenuEnd();

		void onOpenMenuStart();
	}

	public interface CloseMenuListener {
		void onCloseMenuStart();

		void onCloseMenuEnd();
	}

	/**
	 * Opens the menu.
	 */
	public void openMenu() {
		openMenuListener.onOpenMenuStart();
		mMenuDrawer.openMenu();
		openMenuListener.onOpenMenuEnd();
	}

	/**
	 * Opens the menu.
	 * 
	 * @param animate
	 *            Whether open/close should be animated.
	 */
	public void openMenu(boolean animate) {
		openMenuListener.onOpenMenuStart();
		mMenuDrawer.openMenu(animate);
		openMenuListener.onOpenMenuEnd();
	}

	/**
	 * Closes the menu.
	 */
	public void closeMenu() {
		closeMenuListener.onCloseMenuStart();
		mMenuDrawer.closeMenu();
		closeMenuListener.onCloseMenuEnd();
	}

	/**
	 * Closes the menu.
	 * 
	 * @param animate
	 *            Whether open/close should be animated.
	 */
	public void closeMenu(boolean animate) {
		closeMenuListener.onCloseMenuStart();
		mMenuDrawer.closeMenu(animate);
		closeMenuListener.onCloseMenuEnd();
	}

	public boolean isMenuDragEnabled() {
		return mMenuDrawer.isMenuDragEnabled();
	}

	public void setMenuDragEnabled(boolean isDrag) {
		mMenuDrawer.setMenuDragEnableD(isDrag);
	}

	/**
	 * Indicates whether the menu is currently visible.
	 * 
	 * @return True if the menu is open, false otherwise.
	 */
	public boolean isMenuVisible() {
		return mMenuDrawer.isMenuVisible();
	}

	/**
	 * Returns the state of the drawer. Can be one of
	 * {@link MenuDrawer#STATE_CLOSED}, {@link MenuDrawer#STATE_CLOSING},
	 * {@link MenuDrawer#STATE_DRAGGING}, {@link MenuDrawer#STATE_OPENING} or
	 * {@link MenuDrawer#STATE_OPEN}.
	 * 
	 * @return The drawers state.
	 */
	public int getDrawerState() {
		return mMenuDrawer.getDrawerState();
	}

	/**
	 * Set the menu view from a layout resource.
	 * 
	 * @param layoutResId
	 *            Resource ID to be inflated.
	 */
	public void setMenuView(int layoutResId) {
		mMenuContainer.removeAllViews();
		mMenuView = mActivity.getLayoutInflater().inflate(layoutResId,
				mMenuContainer, false);
		mMenuContainer.addView(mMenuView);
	}

	/**
	 * Set the menu view to an explicit view.
	 * 
	 * @param view
	 *            The menu view.
	 */
	public void setMenuView(View view) {
		setMenuView(view, new LayoutParams(LayoutParams.MATCH_PARENT,
				LayoutParams.MATCH_PARENT));
	}

	/**
	 * Set the menu view to an explicit view.
	 * 
	 * @param view
	 *            The menu view.
	 * @param params
	 *            Layout parameters for the view.
	 */
	public void setMenuView(View view, LayoutParams params) {
		mMenuView = view;
		mMenuContainer.removeAllViews();
		mMenuContainer.addView(view, params);
	}

	/**
	 * Returns the menu view.
	 * 
	 * @return The menu view.
	 */
	public View getMenuView() {
		return mMenuView;
	}

	/**
	 * Set the content from a layout resource.
	 * 
	 * @param layoutResId
	 *            Resource ID to be inflated.
	 */
	public void setContentView(int layoutResId) {
		switch (mDragMode) {
		case MenuDrawer.MENU_DRAG_CONTENT:
			mContentContainer.removeAllViews();
			LayoutInflater inflater = mActivity.getLayoutInflater();
			inflater.inflate(layoutResId, mContentContainer, true);
			break;

		case MenuDrawer.MENU_DRAG_WINDOW:
			mActivity.setContentView(layoutResId);
			break;
		}
	}

	/**
	 * Set the content to an explicit view.
	 * 
	 * @param view
	 *            The desired content to display.
	 */
	public void setContentView(View view) {
		setContentView(view, new ViewGroup.LayoutParams(
				LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
	}

	/**
	 * Set the content to an explicit view.
	 * 
	 * @param view
	 *            The desired content to display.
	 * @param params
	 *            Layout parameters for the view.
	 */
	public void setContentView(View view, LayoutParams params) {
		switch (mDragMode) {
		case MenuDrawer.MENU_DRAG_CONTENT:
			mContentContainer.removeAllViews();
			mContentContainer.addView(view, params);
			break;

		case MenuDrawer.MENU_DRAG_WINDOW:
			mActivity.setContentView(view, params);
			break;
		}
	}

	/**
	 * Returns the views current state.
	 * 
	 * @return Returns a Parcelable object containing the MenuDrawer's current
	 *         state.
	 */
	public Parcelable onSaveDrawerState() {
		Bundle state = new Bundle();
		state.putParcelable(STATE_LAYOUT, mMenuDrawer.saveState());
		return state;
	}

	/**
	 * Called to restore the MenuDrawer's state that has previously been
	 * generated with {@link #onSaveDrawerState()}.
	 * 
	 * @param in
	 *            The state that had previously been returned by
	 *            {@link #onSaveDrawerState()}.
	 */
	public void onRestoreDrawerState(Parcelable in) {
		mRestoredState = (Bundle) in;
		mMenuDrawer.restoreState(mRestoredState.getParcelable(STATE_LAYOUT));
	}
}
